package com.pdf.utils;

import com.converters.BeanConverter;
import com.domain.PdfUser;
import com.domain.WriteData;
import com.fonts.AbstractFont;
import com.fonts.DefaultFont;
import com.fonts.FangSontFont;
import com.fonts.ZnHeiFont;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.reflect.ReflectUtil;
import com.write.WriteUtil;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.List;

/**
 * pdf导出工具类
 */
@Data
public final class EasyPdf {
    static {
        // 注册内部字体
        AbstractFont.register(ZnHeiFont.class.getName(), new ZnHeiFont());
        AbstractFont.register(DefaultFont.class.getName(), new DefaultFont());
        AbstractFont.register(FangSontFont.class.getName(), new FangSontFont());
    }

    public static EasyPdf.Builder of(OutputStream out) {
        return of(out, false);
    }

    public static EasyPdf.Builder of(OutputStream out, Boolean lock) {
        return of(out, lock, new PdfUser());
    }

    public static EasyPdf.Builder of(OutputStream out, Boolean lock, PdfUser pdfUser) {
        return new EasyPdf.Builder(out, lock, pdfUser);
    }

    @Data
    public static class Builder {
        private OutputStream outputStream;
        private OutputStream pdfOutputStream;
        private Document document;
        private AbstractWatermark watermark;
        public static final float MARGIN_LEFT = 50;
        public static final float MARGIN_RIGHT = 50;
        public static final float MARGIN_TOP = 50;
        public static final float MARGIN_BOTTOM = 50;
        private PdfWriter writer;
        private Boolean picWatermark;
        private Boolean lock = false;
        private String userPassword;
        private String adminPassword;
        private PdfUser pdfUser;
        private List<WriteData> writeDataList = new ArrayList<>();
        private String fileName;
        private String filePath;

        Builder(OutputStream outputStream, Boolean lock, PdfUser pdfUser) {
            this.outputStream = outputStream;
            String uuid = UUID.randomUUID().toString().replaceAll("-", "") + ".pdf";
            this.fileName = uuid;
            this.filePath = uuid;
            try {
                this.pdfOutputStream = new FileOutputStream(this.filePath);
                // 1.新建document对象 建立一个Document对象
                this.document = new Document(PageSize.A4);
                this.writer = PdfWriter.getInstance(this.document, this.pdfOutputStream);
                this.writer.setViewerPreferences(PdfWriter.PageModeUseThumbs);
                // 设置页边距
                document.setMargins(MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM);
                this.lock = lock;
                if (this.lock) {
                    if (Objects.nonNull(pdfUser)) {
                        if (!StringUtils.isEmpty(pdfUser.getUserPassword())) {
                            this.userPassword = pdfUser.getUserPassword();
                        }
                        if (!StringUtils.isEmpty(pdfUser.getAdminPassword())) {
                            this.adminPassword = pdfUser.getAdminPassword();
                        }
                    }

                    this.writer.setEncryption(this.userPassword.getBytes(StandardCharsets.UTF_8),
                            this.adminPassword.getBytes(StandardCharsets.UTF_8),
                            PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY, PdfWriter.ENCRYPTION_AES_128);
                }
                this.pdfUser = pdfUser;
                PDFBuilder builder = new PDFBuilder();
                this.writer.setPageEvent(builder);
                // 3.打开文档
                this.document.open();
            } catch (DocumentException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        public EasyPdf.Builder addWatermark(Class watermarkClass) {
            this.watermark = ReflectUtil.newInstanceParam(watermarkClass, DefaultFont.class);
            return this;
        }

        public EasyPdf.Builder addWatermark(AbstractWatermark o) {
            this.watermark = o;
            return this;
        }

        public EasyPdf.Builder addSpecWatermasrk(Class... className) {
            this.watermark = ReflectUtil.newInstanceParam(className);
            return this;
        }

        public void doWrite(Object data) {
            try {
                Map<String, PdfPTable> pdf_table_map = new HashMap<>();
                this.writeDataList = BeanConverter.beanToWriteData(data);
                String previousGroup = "_";
                Iterator<WriteData> iterator = this.writeDataList.iterator();
                WriteData info = new WriteData();
                while (iterator.hasNext()) {
                    info = iterator.next();

                    if (!previousGroup.equalsIgnoreCase("_")
                            && Objects.isNull(pdf_table_map.get(info.getGroup()))) {
                        PDFUtils.addLine(document, info.getLineEnum(), pdf_table_map, previousGroup);
                    }
                    else if (!previousGroup.equalsIgnoreCase("_")
                            && !StringUtils.isEmpty(previousGroup)
                            && StringUtils.isEmpty(info.getGroup())) {
                        PDFUtils.addLine(document, info.getLineEnum(), pdf_table_map, previousGroup);

                    }
                    WriteUtil.writeDataHandle(info.getDataType(), info.getWriteHandleClassName()).write(this.document, info, pdf_table_map, previousGroup);
                    previousGroup = info.getGroup();
                }
                if (!previousGroup.equalsIgnoreCase("_") && !StringUtils.isEmpty(previousGroup)) {
                    PDFUtils.addLine(document, info.getLineEnum(), pdf_table_map, previousGroup);
                }
                document.close();
                writer.close();
                if (Objects.nonNull(this.watermark)) {
                    this.watermark.addWatermark(this.filePath, this.outputStream, this.lock, this.pdfUser);
                } else {
                    try (FileInputStream fis = new FileInputStream(this.filePath)) {
                        // 创建一个缓冲区
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        // 读取源文件内容到缓冲区
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            // 将缓冲区内容写入目标文件
                            this.outputStream.write(buffer, 0, bytesRead);
                        }
                        // 刷新输出流，确保所有数据都被写入文件（对于FileOutputStream来说，这一步通常是可选的，因为close方法会调用flush）
                        this.outputStream.flush();
                    }
                    File file = new File(this.filePath);
                    file.delete();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (null != this.pdfOutputStream) {
                    try {
                        this.pdfOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}
